Skip to content

Tolerate shutdown message after channel is closed #646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

dnadoba
Copy link
Collaborator

@dnadoba dnadoba commented Nov 8, 2022

Motivation

A channel can close unexpectedly if something goes wrong. We may in the meantime have scheduled the connection for graceful shutdown but the connection has not yet seen the message. We need to still accept the shutdown message and just ignore it if we are already closed.

Modification

  • ignore calls to shutdown if the channel is already closed
  • add a test which would previously crash because we have transition from the closed state to the closing state and we hit the deinit precondition
  • include the current state in preconditions if we are in the wrong state

Result

We don’t hit the precondition in the deinit in the scenario described above and have more descriptive crashes if something still goes wrong.

### Motivation
A channel can close unexpectedly if something goes wrong. We may in the meantime have scheduled the connection for graceful shutdown but the connection has not yet seen the message. We need to still accept the shutdown message and just ignore it if we are already closed.

### Modification
- ignore calls to shutdown if the channel is already closed
- add a test which would previously crash because we have transition from the closed state to the closing state and we hit the deinit precondition
- include the current state in preconditions if we are in the wrong state

### Result

We don’t hit the precondition in the deinit in the scenario described above and have more descriptive crashes if something still goes wrong.
@dnadoba dnadoba added the 🔨 semver/patch No public API change. label Nov 8, 2022
Comment on lines 282 to 286
case .closed:
// we are already closed and we need to tolerate this
break
case .initialized, .closing:
preconditionFailure("invalid state \(self.state)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also support shutdown calls if we are already closing. No reason to crash here. Also do we want to support shutdown, if we haven't even connected? We could go straight to closed, which is the only way to drop a connection currently.

Copy link
Collaborator Author

@dnadoba dnadoba Nov 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, changed in 1a51229

Comment on lines 50 to 57
let connection = HTTP2Connection(
channel: embedded,
connectionID: 0,
decompression: .disabled,
delegate: TestHTTP2ConnectionDelegate(),
logger: logger
)
_ = connection.start()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't you use the existing internal start method? I think we should keep the plain start() method internal.

Copy link
Collaborator Author

@dnadoba dnadoba Nov 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that the only way for the the future returned by HTTP2Connection.start() to fulfill is by sending ByteBuffers of HTTP2Frames through the Channel. HTTP2Frame de/encoding is currently not publicly available.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we don't need that future, or am I missing something here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do need it as only its success value contains the HTTP2Connection instance. We otherwise don't have access to it and therefore can't call .shutdown().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stupid me... makes sense. can we rename it to start0 though. to make sure it is called on el, if someone else internally tries to call it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure changed in ba76fe4

@dnadoba dnadoba force-pushed the dn-tollerate-shutdown-after-closed branch from 653858d to 1a51229 Compare November 8, 2022 09:19
@dnadoba dnadoba requested a review from fabianfett November 8, 2022 12:16
Comment on lines +286 to +287
case .initialized, .starting:
preconditionFailure("invalid state \(self.state)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for us not to support going from initialized straight to closed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an invalid transition. We require a call to start() before any other interaction which transitions to the starting case. Further interaction is only allowed after the promise returned by .start() is completed which transitions to either the .active or .closed state. This makes sure we don't violate this contract.

And wait for connection start to fail
Copy link
Collaborator

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One very minor nit, otherwise this looks great!

@@ -164,15 +164,23 @@ final class HTTP2Connection {
return promise.futureResult
}

private func start() -> EventLoopFuture<Int> {
func start0() -> EventLoopFuture<Int> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: should we give this an underscore?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 510d635

@dnadoba dnadoba merged commit fd03ed0 into swift-server:main Nov 8, 2022
@dnadoba dnadoba deleted the dn-tollerate-shutdown-after-closed branch November 8, 2022 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 semver/patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants